<html>
<head>
<title>Blobs</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">

<script id="screen_clamp_shader-vs" type="x-shader/x-vertex">
    attribute vec2 aVertexPosition;
    varying vec2 vTextureCoord;
    void main(void) {
        gl_Position = vec4(aVertexPosition,0.0, 1.0);
		vTextureCoord = aVertexPosition;
    }
</script>

<script id="screen_clamp_shader-fs" type="x-shader/x-fragment">
    precision mediump float;
    varying vec2 vTextureCoord;
    uniform mat4 mView;
	uniform vec2 mTime;
	
	#define EPSILON 0.001
	#define VIEW_LEN 10.0

	float DistanceToSphere( vec3 center, float radius, vec3 pos )
	{
		return length ( center - pos ) - radius;
	}
	float DistanceToPlane( vec4 plane, vec3 pos )
	{
		return dot(plane.xyz, pos ) - plane.w;
	}
	float DistanceToAABB( vec3 center, vec3 extents, vec3 pos )
	{
		vec3 delta = abs ( pos - center ) - extents;
		return max ( delta.x, max ( delta.y, delta.z ) );
	}

	/*
	float DistanceAtPoint( vec3 pos)
	{
		float distance = DistanceToSphere(vec3(0.5,0.5,0.5), 0.1, pos);
		float distance2 = DistanceToAABB(vec3(0.53,0.54,0.55), vec3(0.1,0.1,0.1), pos);
		
		float lerp = abs(sin(mTime.x*0.3));
		float distance3 = DistanceToSphere(vec3(0.3,0.3,0.3), 0.03, sin(mTime.x+pos));
		return min(distance*lerp + distance2*(1.0-lerp),distance3);//sin(Time*0.3));
	}
	*/
	float DistanceAtPoint( vec3 pos)
	{
		//pos = fract(pos*0.2)*5.0;
		float adjust = mTime.x*0.1;
		vec2 ajustPos = pos.xy;
		float lerp = (sin(ajustPos.y+adjust*11.0)+cos(ajustPos.y+adjust*23.0)+sin(ajustPos.x+adjust*13.0)+cos(ajustPos.x+adjust*17.0))*0.1;
		
		float outerD = DistanceToAABB(vec3(0.0,0.0,0.0), vec3(1.2,1.2,1.2), pos);
		float planeD = DistanceToPlane(vec4(0.0,0.0,1.0,-1.3+lerp), pos);
		//return planeD;
		
		float innerSphereD = DistanceToSphere(vec3(0.5,0.5,0.5), 0.5, fract(pos));
		float innerSphereD2 = DistanceToSphere(vec3(0.5,0.5,0.5), 0.2, fract(pos+0.08));
		float outerinnerBoxD = DistanceToAABB(vec3(0.5,0.5,0.5), vec3(0.2,0.2,0.2), fract(pos-0.08));
		float lerpVal = abs(sin(floor(pos.x)+mTime.x*0.4));
		innerSphereD2 = innerSphereD2*lerpVal + outerinnerBoxD*(1.0-lerpVal);
		
		float innerBoxD = DistanceToAABB(vec3(0.0,0.0,0.0), vec3(1.5,0.2,1.5), (pos));
		float innerBoxD2 = DistanceToAABB(vec3(0.0,0.0,0.0), vec3(1.5,1.4,0.7), pos);//(pos+vec3(0.0,0.0,-lerp)));
		
		return min(planeD,max(outerD, min(-min(innerSphereD, min(innerBoxD, innerBoxD2)),innerSphereD2)));
	}
	vec3 NormalAtPoint( vec3 pos, float distance )
	{
		vec3 n = vec3(0.0,0.0,0.0);
		vec3 offset = vec3 ( EPSILON, 0.0, 0.0 );
		
		n.x = DistanceAtPoint ( pos + offset.xyz ) - distance;
		n.y = DistanceAtPoint ( pos + offset.yxz ) - distance;
		n.z = DistanceAtPoint ( pos + offset.yzx ) - distance;
		
		return normalize ( n );
	}
	vec3 applyFog( in vec3  rgb,      // original color of the pixel
				   in float distance, // camera to point distance
				   in vec3  rayOri,   // camera position
				   in vec3  rayDir,
					in vec3 litDir)  // camera to point vector
	{
		float c = 0.006;
		float b = 2.4;
		float fogAmount = c * exp(-rayOri.z*b) * (1.0-exp( -distance*rayDir.z*b ))/rayDir.z;
		float sunAmount = max( dot( rayDir, litDir ), 0.0 );
		vec3  fogColor  = mix( vec3(0.5,0.6,0.7), // bluish
                           vec3(1.0,0.9,0.7), // yellowish
                           pow(sunAmount,4.0) );
						   
		return mix( rgb, fogColor, min(fogAmount,1.0) );
	}
    void main(void) {
	
		float ScreenRatio = 3.0/4.0;
		float FOVlength = 2.0;
	
		vec3 rayDir = vec3 ( 0.0, 0.0, 0.0 );
		rayDir += mView[0].xyz * vTextureCoord.x;
		rayDir += mView[1].xyz * vTextureCoord.y * ScreenRatio;
		rayDir += mView[2].xyz * FOVlength;
		
		vec3  rayPosition = mView[3].xyz;
		rayDir = normalize ( rayDir );
		vec3 oldRay = rayDir;
		vec3 oldPosition = rayPosition+vec3(0.0,10.0,0.0);
			
		vec3  sphereColor = vec3(1.0,1.7,0.5);
		vec3  sphereColor2 = vec3(1.4,1.1,1.5);
		vec3  color = vec3(0.0,0.0,0.0);
		float prevLen = FOVlength;
		float totalDistance = 0.0;
		float bounceDec = 1.0;
		float firstHit = 1.0;
		//vec3 lightPos = vec3(0.0, 0.0, 0.0 );
		vec3 lightPos = vec3(cos(mTime.x)*4.0,sin(mTime.x)*4.0,2.0);
		for (int i=0; i < 128; i++)
		{
			if (totalDistance > VIEW_LEN)
				break;
			float distance = DistanceAtPoint(rayPosition);
			totalDistance += distance;
			if (distance < EPSILON)
			{
				vec3 normal = -NormalAtPoint(rayPosition,distance);
				vec3 matID = fract(floor(rayPosition)*0.1);
				
				
				vec3 lightDir = (rayPosition-lightPos);
				float lightDirLen = length(lightDir);
				float distFromCam = dot(oldRay, rayPosition-mView[3].xyz);
				float attunation = max(1.0-distFromCam*0.1,0.0);
				//attunation = min(attunation, max(0.05, dot(normal, lightDir/lightDirLen)));
				attunation = max(0.0, dot(normal, lightDir/lightDirLen));
				
				//color += attunation*(sphereColor*attunation + sphereColor2*(1.0-attunation))*bounceDec;
				color += attunation*sphereColor*bounceDec;
				
				rayDir = reflect(rayDir,normal);
				bounceDec *= 0.3;
				distance = prevLen*2.0;
				float distanceInNormalDir = DistanceAtPoint(rayPosition-normal*0.1)*10.0;
				color *= distanceInNormalDir;
				
				oldPosition = oldPosition*(1.0-firstHit) + rayPosition*firstHit;
				firstHit = 0.0;
			}
			prevLen = distance;
			rayPosition += distance*rayDir;
		}
		totalDistance = 0.0;
		rayPosition = lightPos;
		rayDir = normalize(oldPosition-lightPos);
		for (int i=0; i < 128; i++)
		{
			if (totalDistance > VIEW_LEN)
				break;
			
			float distance = DistanceAtPoint(rayPosition);
			totalDistance += distance;
			if (distance < EPSILON)
			{
				vec3 lightDir = (oldPosition-rayPosition);
				float lightDirLen = length(lightDir);
				color *= min(max(0.4,1.0-lightDirLen*10.0),1.0);
				break;
			}
			rayPosition += distance*rayDir;
		}
		totalDistance = length(oldPosition-mView[3].xyz);
		float adj = pow(totalDistance/VIEW_LEN,2.0);
		color = color*(1.0-adj);
		
		color = applyFog(color,totalDistance,mView[3].xyz, oldRay, normalize(lightPos-mView[3].xyz));
		
	
		gl_FragColor = vec4(color,1.0);
    }
</script>


<script type="text/javascript">

    var gl;
    var screenClampShaderProgram;
    var squareVertexPositionBuffer;
	var startTime;
	var canvas;
	var mousePos = [0.0,0.0];
    function initGL(canvas) {
        try {
            gl = canvas.getContext("experimental-webgl");
            gl.viewportWidth = canvas.width;
            gl.viewportHeight = canvas.height;
        } catch (e) {
        }
        if (!gl) {
            alert("Could not initialise WebGL, sorry :-(");
        }
    }

    function getShader(gl, id) {
        var shaderScript = document.getElementById(id);
        if (!shaderScript) {
            return null;
        }

        var str = "";
        var k = shaderScript.firstChild;
        while (k) {
            if (k.nodeType == 3) {
                str += k.textContent;
            }
            k = k.nextSibling;
        }

        var shader;
        if (shaderScript.type == "x-shader/x-fragment") {
            shader = gl.createShader(gl.FRAGMENT_SHADER);
        } else if (shaderScript.type == "x-shader/x-vertex") {
            shader = gl.createShader(gl.VERTEX_SHADER);
        } else {
            return null;
        }

        gl.shaderSource(shader, str);
        gl.compileShader(shader);

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            alert(gl.getShaderInfoLog(shader));
            return null;
        }

        return shader;
    }

	function initScreenClampShader() {
        var fragmentShader = getShader(gl, "screen_clamp_shader-fs");
        var vertexShader = getShader(gl, "screen_clamp_shader-vs");

        screenClampShaderProgram = gl.createProgram();
        gl.attachShader(screenClampShaderProgram, vertexShader);
        gl.attachShader(screenClampShaderProgram, fragmentShader);
        gl.linkProgram(screenClampShaderProgram);

        if (!gl.getProgramParameter(screenClampShaderProgram, gl.LINK_STATUS)) {
            alert("Could not initialise shaders");
        }

        gl.useProgram(screenClampShaderProgram);

        screenClampShaderProgram.vertexPositionAttribute = gl.getAttribLocation(screenClampShaderProgram, "aVertexPosition");
        gl.enableVertexAttribArray(screenClampShaderProgram.vertexPositionAttribute);

        screenClampShaderProgram.mView = gl.getUniformLocation(screenClampShaderProgram, "mView");
		screenClampShaderProgram.Time = gl.getUniformLocation(screenClampShaderProgram, "mTime");
	}

    function initSquareBuffer() {
        squareVertexPositionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
        vertices = [
             1.0,  1.0,
            -1.0,  1.0,
             1.0, -1.0,
            -1.0, -1.0
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        squareVertexPositionBuffer.itemSize = 2;
        squareVertexPositionBuffer.numItems = 4;
    }
	
	function updateScene() {
	}
	function drawScene() {
		
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
		gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    	
		gl.clearColor(0.0, 0.0, 0.0, 0.0);
		gl.clearDepth(1.0);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
		
		
        gl.useProgram(screenClampShaderProgram);
	    gl.uniformMatrix4fv(screenClampShaderProgram.mView, false,
		[
			Math.cos(mousePos[0]),Math.sin(mousePos[0]),0,0,
			0,0,1,0,
			-Math.sin(mousePos[0]),Math.cos(mousePos[0]),0,0,
			mousePos[1],0,0,1
		]);
		var d=new Date();
		var t=(d.getTime()-startTime.getTime())/1000;
		gl.uniform2fv(screenClampShaderProgram.Time, [t,0]);
		gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
		gl.vertexAttribPointer(screenClampShaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
		
		gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
    	
    }
	

    function tick() {
        requestAnimFrame(tick);
		updateScene();
        drawScene();
    }
	

    function handleMouseMove(event) {
		mousePos[0] = -((event.clientX/canvas.width)*2.0-1.0)*10;
		mousePos[1] = ((event.clientY/canvas.height)*2.0-1.0)*10;
	}
    function webGLStart() {
        canvas = document.getElementById("blob-canvas");
		
        document.onmousemove = handleMouseMove;
		
        initGL(canvas);
        initScreenClampShader();
        initSquareBuffer();
		startTime = new Date();
		tick();       
    }
	
	window.requestAnimFrame = (function() {
	  return window.requestAnimationFrame ||
			 window.webkitRequestAnimationFrame ||
			 window.mozRequestAnimationFrame ||
			 window.oRequestAnimationFrame ||
			 window.msRequestAnimationFrame ||
			 function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
			   window.setTimeout(callback, 1000/60);
			 };
	})();


</script>
</head>
<body onload="webGLStart();">
    <canvas id="blob-canvas" style="border: none;" width="640" height="480"></canvas>
</body>
</html>
